Saúde Bucal - material suplementar

Procedimentos metodológicos

A construção do modelo de PDFT para saúde bucal baseado em necessidades foi elaborado a partir das orientações de Asamani et al. (2021) que sugerem algumas etapas, que serão descritas com maior detalhamento a seguir:

  1. Definição de escopo da metodologia em termos de cobertura jurisdicional, objetivos e horizonte temporal;
  2. Análise da necessidade de saúde bucal com base em aspectos da demografia, epidemiologia e serviços destinados da população a local;
  3. Tradução do número de serviços em número de profissionais necessários;
  4. Exploração das implicações em termos de recursos, como a oferta atual de profissionais e custos;
  5. Análise de sensibilidade para simulação de parâmetros de incerteza;
  6. Validação de modelo por meio de consulta a stakeholders.

Preparando ambiente do R

Carregando os pacotes necessários para o R.

options(scipen = 999)

library(readxl)
library(tidyverse)
library(DT)
library(RODBC)
library(dplyr)
library(sf)
library(ggplot2)
library(geobr)
library(scales)

A) Aplicação reduzida para um município

1. Definição de escopo

A metodologia tem como escopo estimar a força de trabalho de cirurgiões dentistas e equipe auxiliar atual e necessária para atuação em municípios brasileiros, a nível de atenção primária, em procedimentos de natureza individual e coletiva, bem como três três procedimentos de natureza especializada: endodontia, prótese e periodontia.

2. Análise das necessidades de saúde bucal

A análise da necessidade de saúde bucal (NSB) foi construída a partir de três elementos: distribuição da população por faixa etária (P), prevalência de condições de saúde bucal (H) e procedimentos per capita (S).

Onde:

  • NSB = Necessidade de saúde bucal contabilizada pelo número de serviços do tipo t destinados ao atendimento de população de faixa etária i e localidade l;
  • P = População por faixa-etária i de uma localidade l;
  • H = Prevalência de condições de saúde bucal que levam à necessidade de procedimentos do tipo t por faixa-etária i e dentro de uma localidade l;
  • S = Procedimentos per capita do tipo t, para cada faixa-etária i e dentro de uma localidade l.

2.1. Levantando a faixa etária (P)

A distribuição por faixa etária foi construída com base em dados do último censo (2022) e estão disponíveis na página do IBGE.

Para fins de exemplificação, vamos trabalhar apenas com um município como referência: Palmas (TO). Depois escalamos os resultados para o Brasil todo.

pop_tocantins <- read_excel("~/GitHub/saude_bucal/01_dados/pop_tocantins.xlsx")

pop_palmas <- pop_tocantins |> 
                  filter(ibge == "1721000") |> 
                  select(ibge, Município, de_0_a_14_anos,
                         de_15_a_29_anos, de_30_a_59_anos,
                         acima_de_60_anos) |> 
                  gather(key = "faixa", 
                         value = "total",
                         3:6) |> 
                  mutate(faixa = gsub("_"," ",faixa)) |> 
                  mutate(ibge = as.character(ibge)) |> 
                  mutate(ibge = substr(ibge, 1, 6)) |> 
                  mutate(id_faixa = case_when(faixa == "de 0 a 14 anos" ~ 1,
                                              faixa == "de 15 a 29 anos" ~ 2,
                                              faixa == "de 30 a 59 anos" ~ 3,
                                              faixa == "acima de 60 anos" ~ 4))

Plotando a informação de faixa etária. Nota-se que a faixa etária de 30 a 59 anos possui maior frequência em relação às demais.

ordem <- c("de 0 a 14 anos",
           "de 15 a 29 anos",
           "de 30 a 59 anos",
           "acima de 60 anos")

pop_palmas$faixa <- factor(pop_palmas$faixa, levels = ordem)


pop_palmas |> 
  ggplot(aes(x = faixa, y = total)) + geom_col() +
  theme_minimal() + coord_flip() + 
  theme(axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.text = element_text(size = 16)) + 
  ggtitle("Distribuição de faixa etária por município")

Cabe mencionar que o censo possui outras divisões de faixa etárias. No entanto, utilizamos estas para padronizar com os dados do parâmetro H, calculado a partir da pesquisa de SB 2010 (próximo tópico).

2.2. Prevalência de condições de saúde bucal (H)

O Ministério da Saúde - por meio do material Parâmetros Assistenciais para Programação Ambulatorial e Hospitalar no âmbito do SUS (Brasil, 2021), regulamentado pela portaria de consolidação nº1 de 2017 - traz uma série de parâmetros acerca da saúde bucal.

Um dos parâmetros é a cobertura de serviços de saúde. A cobertura foi calculada a partir da pesquisa SB 2010 para cada faixa etária e tipo de procedimento conforme a lógica da tabela baixo.

Fonte: Brasil (2021)
Área Expressões usadas para cálculo de cobertura
Atenção básica NT_1 (Número de dentes necessitando de restauração de 1 superfície) + NT_2 (Número de dentes necessitando de restauração de 2 ou mais superfícies) + NT_6 (Número de dentes necessitando de extração) + NT_7 (Número de dentes necessitando de controle de lesão branca) + NT_8 (Número de dentes necessitando de selante) + P_CALC (Prevalência de cálculo) + P_BR (Prevalência de bolsa rasa). Foi então criada a variável COB_AB. Se COB_AB = 0 o indivíduo não necessita de procedimentos de atenção básica, se COB_AB =1 ele necessita. A cobertura de atenção básica foi então estimada pelo percentual de pessoas com COB_AB=1
Endodontia A cobertura de endodontia foi calculada a partir da variável NT_5 (Número de dentes necessitando de tratamento pulpar + restauração). Foi criada a variável ENDO onde ENDO=0 se NT_5=0 e ENDO=1 se NT_5≠0. Desta forma, a cobertura de endodontia foi então estimada pelo percentual de pessoas com ENDO=1
Periodontia especializada A cobertura de periodontia especializada foi estimada pelo percentual de pessoas com a variável P_BP (Prevalência de bolsa profunda) = 1
Prótese A cobertura de prótese foi calculada pelo percentual de pessoas com a variável NECPROT (Necessidade geral de prótese) ≠ 0

Com base nos valores de cobertura, é possível então calcular qual a cobertura para cada tipo de procedimento e cada faixa etária.

Vamos fazer a leitura dos dados e filtrar para Palmas-TO.

Cabe destacar que, devido à estrutura da SB2010, estes parâmetros foram calculados para capitais de cada estado ou interior da região.

# Lendo dados 

cobertura_sb <-
  read_excel("~/GitHub/saude_bucal/01_dados/cobertura sb.xlsx") |>  
  mutate(ibge = substr(ibge, 1, 6))

# apresentando dados em formato de tabela interativa

cobertura_sb |> 
  filter(ibge == "172100") |> 
  select(-li_cobertura, 
         -ls_cobertura) |> 
  datatable()

Considerando os dados de população e de cobertura, temos os seguintes resultados para o parâmetro (H):

pop_coberta_palmas <- 
  pop_palmas |> 
  left_join(cobertura_sb, by = c("ibge",
                                 "id_faixa")) |> 
  select(ibge, municipio, faixa, id_faixa, 
         total, procedimento, cobertura) |> 
  mutate(populacao_coberta = cobertura * total) |> 
  mutate(populacao_coberta = round(populacao_coberta, 2))

pop_coberta_palmas |> 
  select(ibge, municipio, faixa, 
         total, procedimento, cobertura,
         populacao_coberta) |> 
  datatable()

2.3. Parâmetros normativos de procedimentos per capita (S)

Os cálculos de necessidade normativa por procedimento per capita (S) foram estimados também no caderno de parâmetros de acordo com o quadro a seguir.

Fonte: Brasil (2021)
Área Procedimentos
Atenção básica

No banco de dados do projeto SB BRASIL 2010 foram transformados os códigos 0 (hígido), A (não examinado) e X (excluído) das variáveis CPI17, CPI11, CPI 27, CPI37, CPI31 e CPI47 em 0 e em 1 para as variáveis que tivessem o código 2 (cálculo) e 3 (bolsa rasa).

Ao final essas variáveis foram somadas e a variável NECPERIO_AB criada para armazenar esta soma.

Foram somados os valores referentes as variáveis NT_1, NT_2, NT_6, NT_7 e NT_8 formando a variável NEC_AB. Finalmente foram somadas NECPERIO_AB e NEC_AB para se obter a variável NEC_AB_TOTAL.

Foi então obtida a média da variável NEC_AB_TOTAL estratificada por idade e domínios geográficos (capitais e interior).

Endodontia Foi obtida a média da variável NT_5 (Número de dentes necessitando de tratamento pulpar + restauração) estratificada por idade e domínios geográficos (capitais e interior).
Periodontia

No banco de dados do projeto SB BRASIL 2010 foram transformados os códigos 0 (hígido), A (não examinado) e X (excluído) das variáveis CPI17, CPI11, CPI 27, CPI37, CPI31 e CPI47 em 0 e em 1 para as variáveis que tivessem o código 4 (bolsa profunda).

Ao final essas variáveis foram somadas e a variável NECPERIO_ESPEC criada para armazenar esta soma.

Foi então obtida a média da variável NECPERIO_ESPEC estratificada por idade e domínios geográficos (capitais e interior). Para as idades de 5 e 12 anos esta variável não foi mensurada no projeto SB BRASIL 2010, portanto não há dados de necessidade normativa para a faixa de 0 a 14 anos.

Prótese

A variável NECPROT foi transformada em NUMPROT (número de próteses) como explicitado a seguir:

  • Se NECPROT = 0 (Não necessita), então NUMPROT = 0
  • Se NECPROT = 1 (Parcial 1 maxilar), então NUMPROT = 1
  • Se NECPROT = 2 (Parcial 2 maxilares), então NUMPROT = 2
  • Se NECPROT = 3 (Total 1 maxilar), então NUMPROT = 3
  • Se NECPROT = 4 (Parcial + total), então NUMPROT = 2
  • Se NECPROT = 5 (Total 2 maxilares), então NUMPROT = 2
  • Se NECPROT = 9 (Sem informação), então NUMPROT = 0

Foi então obtida a média da variável NUMPROT estratificada por idade e domínios geográficos (capitais e interior).

Para as idades de 5 e 12 anos esta variável não foi mensurada no projeto SB BRASIL 2010, portanto não há dados de necessidade normativa para a faixa de 0 a 14 anos.

producao_normativa <- 
  read_excel("~/GitHub/saude_bucal/01_dados/cobertura sb.xlsx", 
    sheet = "Produção normativa", 
    col_types = c("text","text", "text", "text", "numeric", 
                  "numeric", "numeric", "numeric")) |>  
  mutate(ibge = substr(ibge, 1, 6))

producao_palmas <- producao_normativa |> 
                      filter(ibge == "172100") |> 
                      select(-municipio, 
                             -li_cobertura,
                             -ls_cobertura,
                             -faixa_etaria)

necessidades_servicos <- pop_coberta_palmas |> 
                            left_join(producao_palmas, 
                                      by = c("ibge",
                                             "id_faixa",
                                             "procedimento")) |> 
                            mutate(nec_servicos = 
                                  populacao_coberta * producao_pc) |> 
                            mutate(nec_servicos = 
                                  round(nec_servicos, 2))

3. Tradução do número de serviços em número de profissionais necessários

A etapa 3 consiste na tradução do número de serviços, calculado pela equação 1, em número de profissionais necessários (NPSB). Para isso, devem ser considerados dois elementos: produtividade (T) e o tempo total disponível (TTD).

Onde:

  • NPSB = Necessidade de profissionais de saúde bucal;
  • NSB = Necessidade de saúde bucal contabilizada pelo número de serviços do tipo t destinados ao atendimento de população de faixa etária i e localidade l;
  • T = Tempo médio de serviços do tipo t;
  • TTD = Tempo total disponível de um profissional ao longo de um ano.

Vamos utilizar como tempo médio de consulta (T) o valor de 25 minutos. Esse valor foi baseado em um levantamento utilizando técnicas de time-motion realizado para aferir o tempo médio de procedimentos executados pela equipe de saúde bucal, em unidades básicas de saúde em São Paulo, Brasil (Bellotti et al, 2024).

O tempo total disponível (TTD) corresponde ao tempo de um profissional deduzidas ausências programáticas (ex.: férias, feriados) e não programáticos (ex.: licenças para tratamento de saúde). Vamos utilizar como referência o TTD estimado em 1576 horas, frequentemente usado para enfermeiros. Infelizmente, não conseguimos localizar estudos que já tenham levatado um TTD para cirurgiões dentistas no Brasil.

Este resultado indica quantos cirurgiões dentistas por mês são necessários ao longo de um ano para cobrir as necessidades da população.

necessidades_prof_palmas <- 
                    necessidades_servicos |>
                          mutate(nec_prof = 
                                   (nec_servicos * 25/60)/1576) |> 
                          mutate(nivel = if_else(
                            procedimento == "Atenção Básica",
                                            "APS",
                                            "AES")) |> 
                          group_by(ibge, municipio, nivel) |> 
                          summarise(necessidade = sum(nec_prof)) |> 
                          mutate(necessidade = round(necessidade, 2))
                          
DT::datatable(necessidades_prof_palmas)       

4. Exploração das implicações em termos de recursos, como a oferta atual de profissionais e custos

O próximo passo consiste em algumas etapas, sendo elas:

  1. Obtenção da oferta de profissionais;
  2. Cálculo de valor líquido da oferta;
  3. Comparação da oferta com necessidades;

4.1. Obtenção da oferta de profissionais

Para acessar a oferta de profissionais, foi necessário realizar uma consulta no Cadastro Nacional de Estabelecimentos de Saúde - tabela Profissionais (CNES-PF). Como profissionais podem ter múltiplos vínculos, padronizou-se a contagem por meio do Full-time equivalent (FTE), tomando como referência a carga horária de contrato.

# codigo para acessar dados de um datalake 

dremio_host <- Sys.getenv("endereco")
dremio_port <- Sys.getenv("port")
dremio_uid <- Sys.getenv("uid")
dremio_pwd <- Sys.getenv("datalake")


channel <- odbcDriverConnect(sprintf("DRIVER=Dremio Connector;
                                     HOST=%s;
                                     PORT=%s;
                                     UID=%s;
                                     PWD=%s;
                                     AUTHENTICATIONTYPE=Basic Authentication;
                                     CONNECTIONTYPE=Direct", 
                         dremio_host, 
                         dremio_port, 
                         dremio_uid, 
                         dremio_pwd))

query <- 'SELECT * FROM "Analytics Layer".Infraestrutura.Profissionais."TSB/ASB e cirurgiões dentistas em APS e AES"'



oferta <- sqlQuery(channel, query, 
                     as.is = TRUE) 

O script abaixo descreve a consulta realizada por meio de SQL. Foram acessados dados de todos os municípios para a COMPETEN (mês e ano) de janeiro de 2024. Foram acessados dados de Cirurgiões dentistas e Técnicos/Auxiliares de Saúde Bucal. Não houve restrição quanto ao vínculo - SUS ou não SUS. Em uma primeira simulação são feitas análises apenas com um recorte de profissionais SUS.

Foram acessados dados dos seguintes tipos de unidade:

  • Atenção Primária à Saúde (APS)
    • 01 - Posto de saúde
    • 02 - Centro de Saúde/Unidade Básica
    • 32 - Unidade Móvel Fluvial
    • 40 - Unidade Móvel Terrestre
  • Atenção Especializada à Saúde (AES)
    • 36 - Clínica/Centro de Especialidade
    • 73 - Pronto atendimento
    • 04 - Policlínica
    • 22 - Consultório Isolado

Não foi incluída a força de trabalho atuante em hospitais, tendo em vista o escopo da metodologia.

Outro ponto que merece atenção em relação ao código abaixo é a padronização da força de trabalho por um métrica chamada full-time equivalent (FTE). O FTE é utilizado para a padronização da força de trabalho tendo em vista que na área de saúde é comum ter profissionais com múltiplos vínculos e cargas horárias.



WITH CH_PROFISSIONAIS AS(

    SELECT CODUFMUN, 
           COMPETEN, 
           PROF_SUS,
           NIVEL, 
           CATEGORIA, 
           HORAAMBULATORIAL,
           HORAHOSPITALAR,
           HORAOUTRO,
           HORAAMBULATORIAL + HORAHOSPITALAR + HORAOUTRO AS HORA 
    FROM(
            SELECT CODUFMUN, 
                COMPETEN, 
                PROF_SUS, 
                    CASE     
                        WHEN TP_UNID = '01' OR 
                             TP_UNID = '02' OR 
                             TP_UNID = '32' OR
                             TP_UNID = '40' THEN  'APS'
                        WHEN TP_UNID = '36' OR
                             TP_UNID = '73' OR
                             TP_UNID = '04' OR
                             TP_UNID = '22' THEN 'ESPECIALIZADO'
                    END NIVEL,
                    CASE 
                        WHEN substr(CBO, 1, 4) = '2232' THEN 'Cirurgião-dentista'
                        WHEN substr(CBO, 1, 4) = '3224' THEN 'Técnico ou Auxiliar de Saúde Bucal'
                    END CATEGORIA,
                    SUM(HORA_AMB) AS HORAAMBULATORIAL, 
                    SUM(HORAHOSP) AS HORAHOSPITALAR, 
                    SUM(HORAOUTR) AS HORAOUTRO  

            FROM Dados.cnes.PF 
                            
            WHERE (substr(CBO, 1, 4) = '2232' OR 
                    substr(CBO, 1, 4) = '3224') AND
                    COMPETEN = '202401'
            GROUP BY CODUFMUN, COMPETEN, NIVEL, PROF_SUS, CBO
    )
)

SELECT p.CODUFMUN, 
       q.municipio, 
       q.uf_sigla, 
       q.latitude, 
       q.longitude, 
       q.cod_regsaud, 
       q.cod_macrorregiao, 
       q.regiao_saude, 
       q.macrorregiao_pad, 
       p.COMPETEN, 
       p.PROF_SUS,
       p.NIVEL, 
       p.CATEGORIA,
       p.HORA, 
       p.HORA/40 AS FTE40 
   FROM CH_PROFISSIONAIS p
   LEFT JOIN 
   "Analytics Layer".Territorial."Municípios - Hierarquia Completa" q
                ON CAST(P.CODUFMUN AS INTEGER) = q.cod_municipio 

Vamos filtrar apenas para acessar dados de Palmas (TO) e profissionais atuando no SUS.

oferta$FTE40 <- as.numeric(oferta$FTE40)

oferta_palmas <- 
  oferta |> 
  filter(CODUFMUN == "172100" & 
         PROF_SUS == "1") |> 
  group_by(CODUFMUN, CATEGORIA, NIVEL) |> 
  summarise(FTE40 = sum(FTE40)) |> 
  filter(NIVEL != "NA") |> 
  mutate(NIVEL = if_else(NIVEL == "APS",
                                  "APS",
                                  "AES"))  

4.2. Cálculo de valor líquido da oferta

O cálculo do valor líquido da oferta é feito por meio da fórmula 3:

Onde:

  • O = Oferta de cirurgiões-dentistas em dada localidade l;
  • E = Estoque de profissionais atuantes em estabelecimentos de saúde me dada localidade l;
  • AD = Percentual da carga de trabalho dedicada a atividades diretas (%);
  • FC = Percentual da carga de trabalho dedicada a um foco clínico (%)

Desta forma, o script abaixo é usado para calcular os valores líquidos.

oferta_palmas <- 
  oferta_palmas |> 
  mutate(FTE_40_direto = FTE40 * 0.80) |> 
  mutate(FTE_40_linha = FTE_40_direto * 0.80)

oferta_palmas
# A tibble: 4 × 6
# Groups:   CODUFMUN, CATEGORIA [2]
  CODUFMUN CATEGORIA                      NIVEL FTE40 FTE_40_direto FTE_40_linha
  <chr>    <chr>                          <chr> <dbl>         <dbl>        <dbl>
1 172100   Cirurgião-dentista             APS      99          79.2         63.4
2 172100   Cirurgião-dentista             AES      33          26.4         21.1
3 172100   Técnico ou Auxiliar de Saúde … APS      78          62.4         49.9
4 172100   Técnico ou Auxiliar de Saúde … AES      16          12.8         10.2

4.3. Implicações de resultados

Por fim, são feitos os cálculos entre oferta e demanda. São aplicadas duas métricas:

  • Resultado Absoluto (RA), que é referente à diferença entre oferta e demanda, conforme equação 4;

  • Resultado Relativo (RR) se refere à diferença percentual entre oferta e demanda, conforme equação 5.

Onde:

  • RA = Resultado absoluto;
  • O = Oferta de cirurgiões-dentistas em dada localidade l;
  • NPSB = Necessidades cirurgiões-dentistas em dada localidade l;

Onde:

  • RR = Resultado relativo;
  • O = Oferta de cirurgiões-dentistas em dada localidade l;
  • NPSB = Necessidades cirurgiões-dentistas em dada localidade l;

Os cálculos são realizados conforme os seguintes scripts:

oferta_palmas_cd <- oferta_palmas |> 
                        filter(CATEGORIA == "Cirurgião-dentista")

oferta_vs_demanda <-
  necessidades_prof_palmas |> 
  left_join(oferta_palmas_cd, 
            by = c("ibge"="CODUFMUN",
                   "nivel" = "NIVEL")) |> 
  mutate(ra = FTE_40_linha - necessidade, 
         rr = (FTE_40_linha/necessidade) * 100) |> 
  mutate(ra = round(ra, 2),
         rr = round(rr, 2))

oferta_vs_demanda |>
  mutate(necessidade = round(necessidade, 2)) |>
  mutate(FTE_40_linha = round(FTE_40_linha, 2)) |> 
  select(ibge, municipio, 
         nivel, necessidade, 
         FTE_40_linha, rr, ra) |> 
  datatable()  

Ao final, sugere-se o cálculo do impacto financeiro desta lacuna. Isso pode ser feito multiplicando o RA ao salário médio de cirurgiões dentistas em Palmas-TO adicionado a um custo trabalhista. De acordo com dados da Pesquisa Nacional de Amostragem por Domicílio contínua (PNADc) do Instituto Brasileiro de Geografia e Estatística (IBGE), o salário médio é na ordem de 4000. Adicionado a um fator para calcular o custo trabalhista (1.42), tem-se os resultados na tabela a seguir.

custo_palmas <- 
  oferta_vs_demanda |> 
  mutate(custo = ra * (-1) * (4000 * 1.42)) |> 
  select(ibge, municipio, nivel, CATEGORIA,
         custo)
  
custo_palmas |> 
  DT::datatable()

B) Aplicação para Brasil

A mesma metodologia utilizada para o munícipio de Palmas-TO será escalada para cada município brasileiro. Abaixo detalhamos a análise estruturada em etapas.

5. Análise das necessidades de saúde bucal para o Brasil

A análise da necessidade de saúde bucal (NSB) foi construída a partir de três elementos: distribuição da população por faixa etária (P), prevalência de condições de saúde bucal (H) e procedimentos per capita (S).

5.1. Levantando a faixa etária (P) - Brasil

A distribuição por faixa etária (P) foi construída com base em dados do último censo (2022) e estão disponíveis na página do IBGE.

pop_brasil <- read_excel("~/GitHub/saude_bucal/01_dados/populacao_municipio_censo_completa.xlsx")

pop_brasil_tratado <- pop_brasil |>
                  select(cod_municipiodv, 
                         ibge_sb, municipio, 
                         de_0_a_14_anos,
                         de_15_a_29_anos, 
                         de_30_a_59_anos,
                         acima_de_60_anos) |> 
                  gather(key = "faixa", 
                         value = "total",
                         4:7) |> 
                  mutate(faixa = gsub("_"," ",
                                      faixa)) |> 
                  mutate(ibge = as.character(ibge_sb)) |> 
                  mutate(ibge = substr(ibge_sb, 1, 6)) |> 
                  mutate(cod_municipiodv = 
                           as.character(cod_municipiodv)) |> 
                  mutate(cod_mun_loc = cod_municipiodv) |> 
                  mutate(cod_municipiodv = 
                           substr(cod_municipiodv, 1, 6)) |> 
                  mutate(id_faixa = 
                           case_when(faixa == "de 0 a 14 anos" ~ 1,
                                     faixa == "de 15 a 29 anos" ~ 2,
                                     faixa == "de 30 a 59 anos" ~ 3,
                                     faixa == "acima de 60 anos" ~ 4))

Plotando a informação de faixa etária. Nota-se que a faixa etária com maior frequência é de 30 a 59 anos.

ordem <- c("de 0 a 14 anos",
           "de 15 a 29 anos",
           "de 30 a 59 anos",
           "acima de 60 anos")

pop_brasil_tratado$faixa <- factor(pop_brasil_tratado$faixa, 
                                   levels = ordem)


pop_brasil_tratado |> 
  ggplot(aes(x = faixa, 
             y = total)) + 
  geom_col() +
  theme_minimal() + 
  coord_flip() + 
  theme(axis.title.x = element_text(size = 16),
        axis.title.y = element_text(size = 16),
        axis.text = element_text(size = 16)) + 
  ggtitle("Distribuição de faixa etária por município")

5.2. Prevalência de condições de saúde bucal (H) - Brasil

Vamos fazer a leitura dos dados de cobertura de procedimentos para cada faixa etária em todos os municípios do Brasil para, em sequência, calcular a prevalência de condições de saúde bucal (H).

Conforme pode ser observado na tabela, as capitais de todos os estados possuem valores de cobertura associados. No entanto, municípios do interior não possuem uma cobertura individualizada, mas sim um parâmetro correspondente ao interior da região (ex.: o município de Formosa-GO fica no interior da Região Centro-Oeste, logo usamos o parâmetro de interior da Região Centro-Oeste).

# Lendo dados 

cobertura_sb <-
  read_excel("~/GitHub/saude_bucal/01_dados/cobertura sb.xlsx",
            sheet = "Cobertura") |>  
  mutate(ibge = substr(ibge, 1, 6))

# apresentando dados em formato de tabela interativa

cobertura_sb |>
  select(-li_cobertura, 
         -ls_cobertura) |> 
  datatable()

No script abaixo, juntamos a população de cada município à respectiva cobertura.

pop_brasil_tratado <-
  pop_brasil_tratado |> 
    mutate(ibge_sb = 
             as.character(ibge_sb)) |> 
    mutate(ibge_sb = 
             str_sub(ibge_sb, 
                     start = 1, 
                     end = 6)) 
    

cobertura_sb$ibge <- as.character(cobertura_sb$ibge)

pop_coberta_br <- 
  pop_brasil_tratado |> 
  left_join(cobertura_sb, 
            by = c("ibge_sb" = "ibge",
                   "id_faixa" = "id_faixa")) |> 
  select(ibge, ibge_sb, 
         municipio.x, 
         faixa, id_faixa, 
         total, procedimento, cobertura, 
         cod_municipiodv, cod_mun_loc) |> 
  rename(municipio = municipio.x) |> 
  mutate(populacao_coberta = cobertura * total) |> 
  mutate(populacao_coberta = round(populacao_coberta, 2))

5.3. Parâmetros normativos de procedimentos per capita (S) - BRASIL

O próximo passo consiste em juntar as tabelas com o população coberta e a necessidade normativa para cada tipo de procedimento (S). Isso levará ao resultado da necessidade de saúde bucal (NSB), que corresponde ao total de serviços necessários para atender cada faixa etária.

producao_normativa_br <- 
  read_excel("~/GitHub/saude_bucal/01_dados/cobertura sb.xlsx", 
    sheet = "Produção normativa", 
    col_types = c("text","text",
                  "text","text",
                  "numeric","numeric",
                  "numeric","numeric")) |>  
    mutate(ibge = substr(ibge, 1, 6))

producao_brasil <- producao_normativa_br |> 
                      select(-municipio, 
                             -li_cobertura,
                             -ls_cobertura,
                             -faixa_etaria)

necessidades_servicos_br <- pop_coberta_br |> 
                            left_join(producao_brasil, 
                                      by = c("ibge" = "ibge",
                                             "id_faixa",
                                             "procedimento")) |> 
                            mutate(nec_servicos = 
                                    populacao_coberta * producao_pc) |> 
                            mutate(nec_servicos = 
                                     round(nec_servicos, 2))

necessidades_servicos_br |> 
  select(cod_municipiodv, municipio, 
         procedimento, faixa, nec_servicos) |> 
  datatable()
Warning in instance$preRenderHook(instance): It seems your data is too big for
client-side DataTables. You may consider server-side processing:
https://rstudio.github.io/DT/server.html

6. Tradução do número de serviços em número de profissionais necessários - BRASIL

Uma vez que sabemos a necessidade de saúde bucal (NSB) com base na distribuição da população por faixa etária (P), prevalência de condições de saúde bucal (H) e procedimentos per capita (S) podemos traduzir o número de serviços em profissionais necessários.

necessidades_prof_br <- 
            necessidades_servicos_br |>
                          mutate(nec_prof = 
                                (nec_servicos * 25/60)/1576) |> 
                          mutate(nivel = if_else(
                            procedimento == "Atenção Básica",
                                           "APS",
                                           "AES")) |> 
                          group_by(ibge, municipio, 
                                   nivel, cod_municipiodv, 
                                   cod_mun_loc) |> 
                          summarise(necessidade = sum(nec_prof)) |> 
                          mutate(necessidade = round(necessidade, 2))
`summarise()` has grouped output by 'ibge', 'municipio', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
DT::datatable(necessidades_prof_br) 
Warning in instance$preRenderHook(instance): It seems your data is too big for
client-side DataTables. You may consider server-side processing:
https://rstudio.github.io/DT/server.html

7. Exploração das implicações em termos de recursos, como a oferta atual de profissionais e custos

O próximo passo consiste em algumas etapas, sendo elas:

  1. Obtenção da oferta de profissionais;
  2. Cálculo de valor líquido da oferta;
  3. Comparação da oferta com necessidades;

7.1. Obtenção da oferta de profissionais

O código abaixo traz o script de acesso a dados do CNES-PF para cirurgiões-dentistas e técnicos/auxiliares de saúde bucal, usando infraestrutura própria.

# codigo para acessar dados de um datalake 

dremio_host <- Sys.getenv("endereco")
dremio_port <- Sys.getenv("port")
dremio_uid <- Sys.getenv("uid")
dremio_pwd <- Sys.getenv("datalake")


channel <- odbcDriverConnect(sprintf("DRIVER=Dremio Connector;
                                     HOST=%s;
                                     PORT=%s;
                                     UID=%s;
                                     PWD=%s;
                                     AUTHENTICATIONTYPE=Basic Authentication;
                                     CONNECTIONTYPE=Direct", 
                         dremio_host, 
                         dremio_port, 
                         dremio_uid, 
                         dremio_pwd))

query <- 'SELECT * FROM "Analytics Layer".Infraestrutura.Profissionais."TSB/ASB e cirurgiões dentistas em APS e AES"'



oferta <- sqlQuery(channel, query, 
                     as.is = TRUE)

O script abaixo agrupa o total da força de trabalho por nível de atenção e município.

oferta$FTE40 <- as.numeric(oferta$FTE40)

oferta_brasil <- 
  oferta |> 
  filter(PROF_SUS == "1") |> 
  group_by(CODUFMUN, CATEGORIA, NIVEL) |> 
  summarise(FTE40 = sum(FTE40)) |> 
  filter(NIVEL != "NA") |> 
  mutate(NIVEL = if_else(NIVEL == "APS","APS","AES")) 

7.2. Cálculo de valor líquido da oferta

O próximo passo consiste na deducação da força de trabalho associada a atividades indiretas, bem como o percentual associado à linha de cuidado.

oferta_brasil <- 
  oferta_brasil |> 
  mutate(FTE_40_direto = FTE40 * 0.80) |> 
  mutate(FTE_40_linha = FTE_40_direto * 0.80)

oferta_brasil
# A tibble: 14,301 × 6
# Groups:   CODUFMUN, CATEGORIA [11,047]
   CODUFMUN CATEGORIA                     NIVEL FTE40 FTE_40_direto FTE_40_linha
   <chr>    <chr>                         <chr> <dbl>         <dbl>        <dbl>
 1 110001   Cirurgião-dentista            APS       2           1.6         1.28
 2 110001   Técnico ou Auxiliar de Saúde… APS       2           1.6         1.28
 3 110002   Cirurgião-dentista            APS      10           8           6.4 
 4 110002   Cirurgião-dentista            AES       4           3.2         2.56
 5 110002   Técnico ou Auxiliar de Saúde… APS      11           8.8         7.04
 6 110002   Técnico ou Auxiliar de Saúde… AES       2           1.6         1.28
 7 110003   Cirurgião-dentista            APS       1           0.8         0.64
 8 110003   Técnico ou Auxiliar de Saúde… APS       1           0.8         0.64
 9 110004   Cirurgião-dentista            APS       5           4           3.2 
10 110004   Cirurgião-dentista            AES       8           6.4         5.12
# ℹ 14,291 more rows

7.3. Implicações de resultados

Agora vamos comparar a força de trabalho disponível com a necessidade. Para isso, utilizamos duas referências, a força de trabalho

7.3.1. Resultados para cirurgiões-dentistas

O script a seguir calcula os resultados para Cirurgiões dentistas.

oferta_brasil_cd <- oferta_brasil |> 
                        filter(CATEGORIA == "Cirurgião-dentista")

cd_oferta_vs_demanda_br <-
  necessidades_prof_br |> 
  left_join(oferta_brasil_cd, 
            by = c("cod_municipiodv"="CODUFMUN",
                   "nivel" = "NIVEL")) 


cd_oferta_vs_demanda_br$FTE_40_linha[is.na(cd_oferta_vs_demanda_br$FTE_40_linha)] <- 0


cd_oferta_vs_demanda_br$FTE40[is.na(cd_oferta_vs_demanda_br$FTE40)] <- 0


cd_oferta_vs_demanda_br$FTE_40_direto[is.na(cd_oferta_vs_demanda_br$FTE_40_direto)] <- 0


cd_oferta_vs_demanda_br <- 
  cd_oferta_vs_demanda_br |> 
  mutate(ra = FTE_40_linha - necessidade, 
         rr = (FTE_40_linha/necessidade) * 100) |> 
  mutate(ra = round(ra, 2),
         rr = round(rr, 2)) |> 
  mutate(necessidade = round(necessidade, 2)) |>
  mutate(FTE_40_linha = round(FTE_40_linha, 2)) |> 
  select(ibge, municipio, 
         nivel, necessidade, 
         FTE_40_linha, rr, ra, cod_municipiodv, 
         cod_mun_loc)

cd_oferta_vs_demanda_br |> 
  DT::datatable()
Warning in instance$preRenderHook(instance): It seems your data is too big for
client-side DataTables. You may consider server-side processing:
https://rstudio.github.io/DT/server.html

Vamos gerar a visualização usando um mapa cloroplético.

mun <- read_municipality(code_muni="all", year=2022)

  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |===                                                                   |   4%
  |                                                                            
  |=====                                                                 |   7%
  |                                                                            
  |========                                                              |  11%
  |                                                                            
  |==========                                                            |  15%
  |                                                                            
  |=============                                                         |  19%
  |                                                                            
  |================                                                      |  22%
  |                                                                            
  |==================                                                    |  26%
  |                                                                            
  |=====================                                                 |  30%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |==========================                                            |  37%
  |                                                                            
  |=============================                                         |  41%
  |                                                                            
  |===============================                                       |  44%
  |                                                                            
  |==================================                                    |  48%
  |                                                                            
  |====================================                                  |  52%
  |                                                                            
  |=======================================                               |  56%
  |                                                                            
  |=========================================                             |  59%
  |                                                                            
  |============================================                          |  63%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |====================================================                  |  74%
  |                                                                            
  |======================================================                |  78%
  |                                                                            
  |=========================================================             |  81%
  |                                                                            
  |============================================================          |  85%
  |                                                                            
  |==============================================================        |  89%
  |                                                                            
  |=================================================================     |  93%
  |                                                                            
  |===================================================================   |  96%
  |                                                                            
  |======================================================================| 100%
mun <- mun |> 
  mutate(code_muni = 
           as.character(code_muni))

mun2 <- cd_oferta_vs_demanda_br |> 
    left_join(mun, by= c("cod_mun_loc" = "code_muni"))
  
mun_sf <- st_as_sf(mun2)

ggplot() +
  geom_sf(data = mun_sf, aes(fill = rr), 
          color = NA) +
  scale_fill_gradientn(colors = c("#D92B3A", 
                                  "#F2C744", 
                                  "#ADBF69"), 
                       values = rescale(c(0, 
                                          100, 
                                          300)), 
                       limits = c(0, 600)) +  
  labs(fill = "Taxa RR") +  
  theme_void() + 
  facet_grid(~nivel)

De acordo com os resultados das tabelas e do mapa, nós temos alguns poucos municípios que possuem um resultado relativo muito elevado, na ordem de 600%. Isso significa que a oferta é muito superior à necessidade.

Isso ocorre geralmente em municípios muito pequenos. O município de Cachoeira Dourada - MG possui apenas 2706 habitantes. Com bases nos cálculos, a necessidade é de 0,31 profissionais para atenção especializada. No entanto, o município conta com 1,92 profissionais. Por isso a diferença elevada.

Calculando o gap por UF para ter uma noção de quais estados possuem as maiores lacunas de profissionais.

hierarquia_municipios <- read_csv("~/GitHub/saude_bucal/01_dados/hierarquia_atualizada.csv")
Rows: 5570 Columns: 18
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (12): regiao_pad, regiao, uf_pad, uf, uf_sigla, macrorregiao_pad, macror...
dbl  (6): cod_uf, cod_macrorregiao, cod_regsaud, cod_municipio, cod_municipi...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
uf <- hierarquia_municipios |> 
          select(cod_municipiodv, uf_sigla, regiao)

uf$cod_municipiodv <- as.character(uf$cod_municipiodv)

cd_oferta_vs_demanda_br |>
    filter(rr < 200) |> 
    left_join(uf, by = c("cod_mun_loc"="cod_municipiodv")) |> 
    ggplot(aes(x = rr, 
               y = fct_reorder(uf_sigla,rr),
               fill = regiao)) + 
      geom_boxplot() + facet_wrap(~nivel) + 
    theme_minimal()

8. Criando função com alguns parâmetros

Uma das últimas etapas do desenvolvimento de metodologias consiste na

gap_necessidade_demanda <- 
  function(tempo, ttd, pd, pl){

          pop_brasil_tratado <- 
                  pop_brasil |>
                  select(cod_municipiodv, 
                         ibge_sb, municipio, de_0_a_14_anos,
                         de_15_a_29_anos, de_30_a_59_anos,
                         acima_de_60_anos) |> 
                  gather(key = "faixa", 
                         value = "total",
                         4:7) |> 
                  mutate(faixa = gsub("_"," ",faixa)) |> 
                  mutate(ibge = as.character(ibge_sb)) |> 
                  mutate(ibge = substr(ibge_sb, 1, 6)) |> 
                  mutate(cod_municipiodv = 
                           as.character(cod_municipiodv)) |> 
                  mutate(cod_mun_loc = cod_municipiodv) |> 
                  mutate(cod_municipiodv = 
                           substr(cod_municipiodv, 1, 6)) |> 
                  mutate(id_faixa = case_when(
                    faixa == "de 0 a 14 anos" ~ 1,
                    faixa == "de 15 a 29 anos" ~ 2,
                    faixa == "de 30 a 59 anos" ~ 3,
                    faixa == "acima de 60 anos" ~ 4))

pop_brasil_tratado <-
  pop_brasil_tratado |> 
    mutate(ibge_sb = as.character(ibge_sb)) |> 
    mutate(ibge_sb = str_sub(ibge_sb, start = 1, end = 6))

cobertura_sb$ibge <- as.character(cobertura_sb$ibge)

pop_coberta_br <- 
  pop_brasil_tratado |> 
  left_join(cobertura_sb, by = c("ibge_sb" = "ibge",
                                 "id_faixa" = "id_faixa")) |> 
  select(ibge, ibge_sb, municipio.x, faixa, id_faixa, 
         total, procedimento, cobertura, 
         cod_municipiodv, cod_mun_loc) |> 
  mutate(populacao_coberta = cobertura * total) |> 
  mutate(populacao_coberta = round(populacao_coberta, 2))


producao_brasil <- producao_normativa_br |> 
                      select(-municipio, -li_cobertura,
                             -ls_cobertura,-faixa_etaria)

necessidades_servicos_br <- pop_coberta_br |> 
                            left_join(producao_brasil, 
                                      by = c("ibge" = "ibge","id_faixa","procedimento")) |> 
                            mutate(nec_servicos = populacao_coberta * producao_pc) |> 
                            mutate(nec_servicos = round(nec_servicos, 2))
  

necessidades_prof_br <- necessidades_servicos_br |>
                          mutate(nec_prof = 
                                   (nec_servicos * tempo/60)/ttd) |> 
                          mutate(nivel = 
                                   if_else(procedimento == "Atenção Básica","APS","AES")) |> 
                          group_by(ibge, municipio.x, nivel,
                                   cod_municipiodv, cod_mun_loc) |> 
                          summarise(necessidade = sum(nec_prof))



oferta$FTE40 <- as.numeric(oferta$FTE40)

oferta_brasil <- 
  oferta |> 
  filter(PROF_SUS == "1") |> 
  group_by(CODUFMUN, CATEGORIA, NIVEL) |> 
  summarise(FTE40 = sum(FTE40)) |> 
  filter(NIVEL != "NA") |> 
  mutate(NIVEL = if_else(NIVEL == "APS","APS","AES")) 
  

oferta_brasil <- 
  oferta_brasil |> 
  mutate(FTE_40_direto = FTE40 * pd) |> 
  mutate(FTE_40_linha = FTE_40_direto * pl)


oferta_brasil_cd <- oferta_brasil |> 
                        filter(CATEGORIA == "Cirurgião-dentista")

oferta_vs_demanda <-
  necessidades_prof_br |> 
  left_join(oferta_brasil_cd, 
            by = c("cod_municipiodv"="CODUFMUN",
                   "nivel" = "NIVEL")) |> 
  mutate(ra = FTE_40_linha - necessidade, 
         rr = (FTE_40_linha/necessidade) * 100) |> 
  mutate(ra = round(ra, 2),
         rr = round(rr, 2)) |> 
  filter(CATEGORIA != "NA")

oferta_vs_demanda <- oferta_vs_demanda |>
  mutate(necessidade = round(necessidade, 2)) |>
  mutate(FTE_40_linha = round(FTE_40_linha, 2)) |> 
  select(ibge, municipio.x, 
         nivel, necessidade, 
         FTE_40_linha, rr, ra, cod_municipiodv, cod_mun_loc)

}

8.1. Executando a função para um conjunto de vetores

teste <- gap_necessidade_demanda(35, 1776, 0.8, 0.8)
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
#CRIANDO LISTAS DE VETORES

list_ttd <- as.numeric(c("1676", "1776", "1876"))
list_tempo <- as.numeric(c("25", "35", "45", "55"))
list_pd <- as.numeric(c("0.50", "0.60", "0.80", "0.90", "1"))
list_pl <- as.numeric(c("0.50", "0.60", "0.80", "0.90", "1"))


# EXECUTANDO LOOP DA FUNÇÃO COM AS LISTAS DE VETORES
resultado <- list()

for (list_ttd in list_ttd) {
  for (list_tempo in list_tempo) {
    for (list_pd in list_pd) {
      for (list_pl in list_pl) { 
        
        res <- 
          gap_necessidade_demanda(tempo = list_tempo, 
                                  ttd = list_ttd, 
                                  pd = list_pd, 
                                  pl = list_pl)
        
        res <- cbind(res, 
                     ttd = list_ttd, 
                     tempo = list_tempo, 
                     pd = list_pd, 
                     pl = list_pl)
        
        res$atributos <- paste(list_ttd, 
                               list_tempo, 
                               list_pd, 
                               list_pl, sep = "_")
        
        resultado[[length(resultado) + 1]] <- res
      }
    }
  }
}
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
Warning in left_join(pop_brasil_tratado, cobertura_sb, by = c(ibge_sb = "ibge", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 121 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
`summarise()` has grouped output by 'ibge', 'municipio.x', 'nivel',
'cod_municipiodv'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'CODUFMUN', 'CATEGORIA'. You can override
using the `.groups` argument.
# Combina os resultados em um único data frame
gap_resultado <- do.call(rbind, resultado)

Referências